package com.fa4j.code.generate.service.impl;

import com.fa4j.code.generate.model.CodeGenerateCmd;
import com.fa4j.code.generate.model.CodeGenerateFile;
import com.fa4j.code.generate.model.CodeTemplateType;
import com.fa4j.code.generate.service.CodeGenerateService;
import com.fa4j.code.generate.service.generator.CodeGenerator;
import com.fa4j.code.generate.service.generator.freemarker.FreemarkerCodeGenerator;
import com.fa4j.code.mate.model.*;
import com.fa4j.code.mate.repository.CodeMateRepository;
import com.fa4j.common.base.exception.CommonRequestError;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@Validated
@RequiredArgsConstructor
public class CodeGenerateServiceImpl implements CodeGenerateService {

    private final Map<CodeTemplateType, CodeGenerator> codeGeneratorMap = new HashMap<>();
    private final CodeMateRepository codeMateRepository;


    @Override
    public List<CodeGenerateFile> generate(CodeGenerateCmd cmd) {
        CodeMateProject project = codeMateRepository.findProjectByProjectName(cmd.getProjectName())
                .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("项目"));
        Map<String, CodeMateModel> modelMap = project.getModuleList().stream()
                .flatMap(m -> m.getModelList().stream())
                .collect(Collectors.toMap(m->m.getId().toString(), Function.identity()));
        CodeGenerator generator = getCodeGenerator(cmd.getTemplateType());
        Map<String, Object> context = new HashMap<>();
        context.put("project", project);
        context.put("generateParams", cmd.getGenerateParams());
        context.put("models", modelMap);
        log.info("{},开始生成代码", cmd.getProjectName());
        List<CodeGenerateFile> generateFiles = new ArrayList<>(generator.generate(CodeMateType.PROJECT, context));
        for (CodeMateModule module : project.getModuleList()) {
            context.put("module", module);
            generateFiles.addAll(generator.generate(CodeMateType.MODULE, context));
            for (CodeMateModel model : module.getModelList()) {
                context.put("model", model);
                generateFiles.addAll(generator.generate(CodeMateType.MODEL, context));
                for (CodeMateMethod method : model.getMethodList()) {
                    context.put("method", method);
                    generateFiles.addAll(generator.generate(CodeMateType.METHOD, context));
                }
            }
        }
        log.info("{},结束生成代码", cmd.getProjectName());
        return generateFiles;
    }

    private CodeGenerator getCodeGenerator(CodeTemplateType templateType) {
        if (codeGeneratorMap.containsKey(templateType)) {
            return codeGeneratorMap.get(templateType);
        }
        CodeGenerator generator = new FreemarkerCodeGenerator(templateType);
        codeGeneratorMap.put(templateType, generator);
        return generator;
    }

}
